/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <stdio.h>

#include "rdx_programmability.hpp"
#include "rdx_objectmanagement.hpp"
#include "rdx_ilcomp.hpp"




namespace RDX
{
	namespace Intrinsics
	{
		using namespace RDX::Programmability;
		using namespace RDX::ObjectManagement;
		using namespace RDX::ILCompiler;
		using namespace RDX::ILOpcodes;

		const char *OpcodeNames[] =
		{
			"ILOP_invalid",

			"ILOP_debuginfo",

			"ILOP_move",
			
			"ILOP_tovarying",
			"ILOP_tovarying_static",
			"ILOP_pinl",
			"ILOP_pinp",
			"ILOP_incptr",
			"ILOP_objinterior",
			"ILOP_objinterior_notnull",
			"ILOP_objinterior_notnull_persist",

			"ILOP_immediate",
			"ILOP_immediate_ptr",
			"ILOP_immediate_rtp",

			"ILOP_arrayindex",
			"ILOP_call",
			"ILOP_calldelegatebp",
			"ILOP_calldelegateprv",
			"ILOP_callvirtual",
			"ILOP_callinterface",

			"ILOP_verifynotnull",
			"ILOP_zero",
			"ILOP_newinstance",

			"ILOP_exit",

			"ILOP_jump",
			"ILOP_jtrue",
			"ILOP_jfalse",
			"ILOP_jinherits",
	
			"ILOP_tick",
			"ILOP_assertenum",
			"ILOP_assertinherits",
			"ILOP_hash_f",
			"ILOP_hash_p",

			"ILOP_jeq_f",
			"ILOP_jeq_p",
			"ILOP_jne_f",
			"ILOP_jne_p",

			"ILOP_xnullref",
			"ILOP_catch",
			"ILOP_fatal",
			"ILOP_throw",

			"ILOP_hardenstack",

			"ILOP_switch",
			"ILOP_switch_ptr",
			"ILOP_iteratearray",
			"ILOP_iteratearraysub",

			"ILOP_intrinsic",
	
			"ILOP_iadd",
			"ILOP_isub",
			"ILOP_imul",
			"ILOP_idiv",
			"ILOP_imod",
			"ILOP_ineg",
			"ILOP_isx",
			"ILOP_itof",
			
			"ILOP_ilt",
			"ILOP_igt",
			"ILOP_ile",
			"ILOP_ige",
			"ILOP_ieq",
			"ILOP_ine",
	
			"ILOP_fadd",
			"ILOP_fsub",
			"ILOP_fmul",
			"ILOP_fdiv",
			"ILOP_fneg",
			"ILOP_ftoi",
			
			"ILOP_flt",
			"ILOP_fgt",
			"ILOP_fle",
			"ILOP_fge",
			"ILOP_feq",
			"ILOP_fne",

			"ILOP_jccp",
			"ILOP_jccf",
		};

		static const char *FlagsToMoveMode(LargeInt flags)
		{
			static char moveMode[7];

			if(flags & ILOP_MOVE_SRC_PARENT_FRAME)
				moveMode[0] = 'p';
			else if(flags & ILOP_MOVE_SRC_TYPE_DEFAULT)
				moveMode[0] = 'd';
			else
				moveMode[0] = 'l';
	
			if(flags & ILOP_MOVE_SRC_DEREF)
				moveMode[1] = '*';
			else
				moveMode[1] = 'v';

			moveMode[2] = '-';
			moveMode[3] = '>';
	
			if(flags & ILOP_MOVE_DEST_PARENT_FRAME)
				moveMode[4] = 'p';
			else
				moveMode[4] = 'l';

			if(flags & ILOP_MOVE_DEST_DEREF)
				moveMode[5] = '*';
			else
				moveMode[5] = 'v';
			moveMode[6] = '\0';

			return moveMode;
		}


		void DisassembleMethod(void *vf, Method *m)
		{
			FILE *f = static_cast<FILE *>(vf);
			const ILInstruction *instructions = m->_native.ilinstructions;
			LargeInt numInstructions = GCInfo::From(instructions)->numElements;

			fwprintf(f, L"Disassembly for method %s\n", GCInfo::From(m)->gstSymbol->AsChars());
			fwprintf(f, L"Stack capacity: %i\n", m->_native.frameCapacity);
			fwprintf(f, L"Journal:\n");
			if(m->_native.numJournals)
			{
				const UInt8 *cj = m->_native.compactedJournals;
				LargeInt nBytes = GCInfo::From(cj)->numElements;

				for(LargeInt i=0;i<m->_native.numJournals;i++)
				{
					StackJournal j;
					cj += j.Decompress(cj);
					fwprintf(f, L"j %i%s: Instr range %i - %i   Offset %i   Type %s (%s)   p: %i  nna: %i\n", i, (j.isParameter ? L" (Param)" : L""), j.startInstruction, j.endInstruction, j.offset,
						j.vType ? GCInfo::From(j.vType)->gstSymbol->AsChars() : RDX_STATIC_STRING("NULL"), (j.isPointer ? L"Pointer" : L"Value"), j.isPointer ? j.pointerSource : -1, j.isNotNull ? j.notNullInstruction : -1 );
				}
			}
			
			fwprintf(f, L"Exception handlers:\n");
			if(m->_native.translation1)
			{
				for(LargeInt i=0;i<GCInfo::From(m->_native.exHandlers)->numElements;i++)
				{
					fwprintf(f, L"eh %i: Instr %i - %i  --> %i\n", i, m->_native.exHandlers[i].startInstruction,
						m->_native.exHandlers[i].endInstruction, m->_native.exHandlers[i].handlerInstruction);
				}
			}

			LargeInt numLargeArgs = GCInfo::From(m->_native.largeArgs)->numElements;
			LargeInt numCompactArgs = GCInfo::From(m->_native.compactArgs)->numElements;
			
			fwprintf(f, L"Instructions:\n");
			for(LargeInt i=0;i<numInstructions;i++)
			{
				const ILInstruction *ili = instructions + i;
				bool resumeFlag = ((m->_native.ilResumeFlags[i / 8] & (1 << (i & 7))) != 0);

				fprintf(f, "%s i %i: %s ", resumeFlag ? "***" : "   ", i, OpcodeNames[ili->opcode]);

				if(ili->arg.ca >= m->_native.compactArgs && ili->arg.ca < m->_native.compactArgs + numCompactArgs)
				{
					LargeInt numArgs = m->_native.compactArgs + numCompactArgs - ili->arg.ca;
					for(LargeInt j=i+1;j<numInstructions;j++)
					{
						const ILInstruction *ili2 = instructions + j;
						if(ili2->arg.ca >= m->_native.compactArgs && ili2->arg.ca < m->_native.compactArgs + numCompactArgs)
						{
							numArgs = ili2->arg.ca - ili->arg.ca;
							break;
						}
					}
					
					fprintf(f, "CA:");
					for(LargeInt a=0;a<numArgs;a++)
						fprintf(f, "\t%i", ili->arg.ca[a].li);
				}
				else if(ili->arg.la >= m->_native.largeArgs && ili->arg.la < m->_native.largeArgs + numLargeArgs)
				{
					LargeInt numArgs = m->_native.largeArgs + numLargeArgs - ili->arg.la;
					for(LargeInt j=i+1;j<numInstructions;j++)
					{
						const ILInstruction *ili2 = instructions + j;
						if(ili2->arg.la >= m->_native.largeArgs && ili2->arg.la < m->_native.largeArgs + numLargeArgs)
						{
							numArgs = ili2->arg.la - ili->arg.la;
							break;
						}
					}

					fprintf(f, "LA:");
					for(LargeInt a=0;a<numArgs;a++)
						fprintf(f, "\t%i:%i", ili->arg.la[a].ca[0].li, ili->arg.la[a].ca[1].li);
				}
				fprintf(f, "\n");

				if(ili->opcode == ILOP_move)
				{
					fprintf(f, "    (%s)\n", FlagsToMoveMode(ili->arg.ca[4].li));
				}
			}
		}
	}
}